package com.fourforfo.fourmall.cart.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.fourforfo.common.to.ProductSkuInfoTO;
import com.fourforfo.common.to.SkuInfoAndAttrsTO;
import com.fourforfo.common.utils.R;
import com.fourforfo.fourmall.cart.feign.ProductFeignService;
import com.fourforfo.fourmall.cart.interceptor.CartInterceptor;
import com.fourforfo.fourmall.cart.service.CartService;
import com.fourforfo.fourmall.cart.to.SkuInfoTO;
import com.fourforfo.fourmall.cart.vo.CartItemVO;
import com.fourforfo.fourmall.cart.vo.CartVO;
import com.fourforfo.fourmall.cart.vo.UserInfoTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;


@Slf4j
@Service
public class CartServiceImpl implements CartService {

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    ProductFeignService productFeignService;

    //购物车数据在redis中的key前缀
    private final String CART_PREFIX = "fourmall:cart:";

    /**
     * 添加商品到购物车
     * @param skuId
     * @param num 商品数量
     * @return
     */
    @Override
    public CartItemVO addCartItemToCart(Long skuId, Integer num) {
        BoundHashOperations operations = getRedisHashOperations();
        /**
         * 获取该用户的购物车中是否有skuId对于的商品
         * 1、有对应skuId的商品的话就将数据取出并对商品数量取出再保存
         */
        Object object = operations.get(String.valueOf(skuId));
        if(object!=null){
            String preJsonString = object.toString();
            if(!StringUtils.isEmpty(preJsonString)){
                CartItemVO preCartItemVO = JSON.parseObject(preJsonString,CartItemVO.class);
                if(preCartItemVO!=null){
                    preCartItemVO.setCount(preCartItemVO.getCount()+num);
                    preJsonString = JSON.toJSONString(preCartItemVO);
                    operations.put(String.valueOf(skuId),preJsonString);
                    return preCartItemVO;
                }
            }
        }


        //2、redis没有对应skuId的商品就远程查询当前skuId的商品信息
        R r = productFeignService.infoAndAttr(skuId);
        if(r.getCode() != 0){
            return null;
        }
        SkuInfoAndAttrsTO skuInfoAndAttrsTO = r.getData("skuInfo", new TypeReference<SkuInfoAndAttrsTO>() {
        });
        //3、添加商品到购物车
        //3.1 先从redis中获取是否有该商品，有就加数量，没有就添加整个商品
        CartItemVO cartItemVO = new CartItemVO();
        cartItemVO.setSkuId(skuId);
        cartItemVO.setCount(num);
        cartItemVO.setImageUrl(skuInfoAndAttrsTO.getSkuDefaultImg());
        cartItemVO.setTitle(skuInfoAndAttrsTO.getSkuTitle());
        cartItemVO.setPrice(skuInfoAndAttrsTO.getPrice());
        cartItemVO.setCheck(true);
        cartItemVO.setSkuAttr(skuInfoAndAttrsTO.getSaleAttrNameValues());

        String jsonString = JSON.toJSONString(cartItemVO);
        operations.put(String.valueOf(skuId),jsonString);

        return cartItemVO;
    }

    /**
     * 获取当前成功加入购物车的sku信息
     * @param skuId
     * @return
     */
    @Override
    public CartItemVO getSuccessCartItem(Long skuId) {
        BoundHashOperations operations = getRedisHashOperations();
        Object object = operations.get(String.valueOf(skuId));
        if(object!=null){
            String jsonString = object.toString();
            if(!StringUtils.isEmpty(jsonString)){
                CartItemVO cartItemVO = JSON.parseObject(jsonString,CartItemVO.class);
                return cartItemVO;
            }
        }
        return null;
    }

    /**
     * 如果登陆了则获取离线购物车和在线购物车，无需转入参数，service层从ThreadLocal中获取
     * 如果没登录就只获取离线购物车
     */
    @Override
    public CartVO listCartWithUserInfoTO() {
        /**
         * 1. 从CartInterceptor.threadLocal中获取userInfo信息
         *    做null值判断
         */
        UserInfoTO userInfoTO = CartInterceptor.threadLocal.get();
        if(userInfoTO == null){
            log.warn("出现未知错误，获取不到CartIntercepter的threadLocal的userInfoTo信息！");
            return null;
        }else if(StringUtils.isEmpty(userInfoTO.getUserKey())){
            log.warn("出现未知错误，user-key为null！");
            return null;
        }else if(userInfoTO.getXxlSsoUser()!=null
                && StringUtils.isEmpty(userInfoTO.getXxlSsoUser().getUserid())){
            log.warn("获取的XxlSsoUser信息中没有userId信息，请调试排查问题！");
            return null;
        }

        /**
         * 2. 获取离线购物车数据：unLoginCartItems
         */
        CartVO cartVO = new CartVO();
        String unLoginCartKey = CART_PREFIX + userInfoTO.getUserKey();
        BoundHashOperations unLoginOperation = redisTemplate.boundHashOps(unLoginCartKey);
        List<CartItemVO> unLoginCartItems = getCartItems(unLoginOperation);
        /**
         * 3. 如果是未登录状态，直接返回
         */
        if(userInfoTO==null || userInfoTO.getXxlSsoUser()==null){
            if(unLoginCartItems!=null){
                cartVO.setItemVOList(unLoginCartItems);
                return cartVO;
            }else{
                return null;
            }
        }else{
        /**
         * 4. 如果是登录状态，
         *  4.1 在线数据为空，返回离线数据
         *  4.2 在线数据不为空，如果离线数据库为空，直接返回在线数据
         *  4.3 在线数据不为空，如果离线数据库不为空，合并两者，并且保存到redis
         */
            String cartKey = CART_PREFIX+userInfoTO.getXxlSsoUser().getUserid();
            BoundHashOperations loginOperation = redisTemplate.boundHashOps(cartKey);
            List<CartItemVO> cartItems = getCartItems(loginOperation);
            //4.1 在线数据库为null，返回离线数据
            if(cartItems == null){
                if(unLoginCartItems!=null){
                    cartVO.setItemVOList(unLoginCartItems);
                    return cartVO;
                }else{
                    return null;
                }
            }else{
                //4.2 在线数据不为空，离线数据为空，直接返回在线数据
                if(unLoginCartItems==null){
                    cartVO.setItemVOList(unLoginCartItems);
                    return cartVO;
                }else{
                    /**
                     * 4.3 在线数据和离线数据都不为空，合并两者，并且保存到redis
                     */
                    Map<String,String> updateMap = new HashMap<>();//需要更新到在线购物车的数据集合
                    Set<Long> equalIds = new HashSet<>();
                    //4.3.1 删除当前user-key对于的所有离线数据中的数据
                    redisTemplate.delete(unLoginCartKey);

                    //4.3.2 合并两种中相同的数据到cartItems中
                    for (CartItemVO cartItem : cartItems) {
                        for(int i = 0;i<unLoginCartItems.size();i++){
                            CartItemVO unLoginCartItem = unLoginCartItems.get(i);
                            if(cartItem.getSkuId() == unLoginCartItem.getSkuId()){
                                //4.3.2.1 合并两者数量
                                cartItem.setCount(cartItem.getCount()+unLoginCartItem.getCount());
                                updateMap.put(cartItem.getSkuId().toString(),JSON.toJSONString(cartItem));
                                equalIds.add(cartItem.getSkuId());
                                continue;
                            }
                        }
                    }
                    //4.3.3 将剩余的与在线数据不相同的离线数据保存到cartItems中
                    for(int i=0;i<unLoginCartItems.size();i++){
                        CartItemVO unLoginCartItem = unLoginCartItems.get(i);
                        if(!equalIds.contains(unLoginCartItem.getSkuId())){
                            updateMap.put(unLoginCartItem.getSkuId().toString(),JSON.toJSONString(unLoginCartItem));
                            cartItems.add(unLoginCartItem);
                        }
                    }
                    //4.3.4 将新增的离线数据以及修改了count的在线数据 保存到redis中
                    loginOperation.putAll(updateMap);

                    cartVO.setItemVOList(cartItems);
                    return cartVO;

                }

            }

        }

    }

    /**
     * 修改购物项的选中状态
     * @param skuId
     * @param check 1:选中 0:未选中
     */
    @Override
    public void checkItem(Long skuId, Integer check) {
        BoundHashOperations redisHashOperations = getRedisHashOperations();
        String value = (String)redisHashOperations.get(String.valueOf(skuId));
        CartItemVO cartItemVO = JSON.parseObject(value, CartItemVO.class);
        Boolean isChecked = check==1;
        if(isChecked==cartItemVO.getCheck()){
            return;
        }else{
            cartItemVO.setCheck(isChecked);
            redisHashOperations.put(String.valueOf(skuId),JSON.toJSONString(cartItemVO));
            return;
        }


    }

    /**
     * 修改购物项的的数量
     * @param skuId
     * @param num 1:选中 0:未选中
     */
    @Override
    public void countItem(Long skuId, Integer num) {
        BoundHashOperations redisHashOperations = getRedisHashOperations();
        String value = (String)redisHashOperations.get(String.valueOf(skuId));
        CartItemVO cartItemVO = JSON.parseObject(value, CartItemVO.class);

        if(num==cartItemVO.getCount()){
            return;
        }else{
            cartItemVO.setCount(num);
            redisHashOperations.put(String.valueOf(skuId),JSON.toJSONString(cartItemVO));
            return;
        }
    }

    /**
     * 删除购物项
     * @param skuId
     */
    @Override
    public void deleteItem(Long skuId) {
        BoundHashOperations redisHashOperations = getRedisHashOperations();
        redisHashOperations.delete(String.valueOf(skuId));
    }

    /**
     * 获取当前用户的购物商品项
     * @return
     */
    @Override
    public List<CartItemVO> getCurrentUserCartItems() {
        UserInfoTO userInfoTO = CartInterceptor.threadLocal.get();
        if(userInfoTO==null||userInfoTO.getXxlSsoUser()==null){
            return null;
        }
        String cartKey = CART_PREFIX + userInfoTO.getXxlSsoUser().getUserid();
        BoundHashOperations<String, Object, Object> boundHashOps = redisTemplate.boundHashOps(cartKey);
        List<CartItemVO> cartItems = getCartItems(boundHashOps);
        if(cartItems==null || cartItems.size()==0){
            return null;
        }
        List<CartItemVO> result = new ArrayList<>();
        List<Long> ids = new ArrayList<>();

        for (CartItemVO cartItem : cartItems) {
            if(cartItem.getCheck()){
                result.add(cartItem);
                ids.add(cartItem.getSkuId());
            }
        }
        if(result.size()==0){
            return null;
        }

        Map<Long, BigDecimal> map = productFeignService.listPriceByIds(ids);

        if(map==null||map.size()!=result.size()){
            return null;
        }

        for (CartItemVO cartItemVO : result) {
            cartItemVO.setPrice(map.get(cartItemVO.getSkuId()));
        }

        return result;
    }

    /**
     * 从redis中获取当前operation的CartItem数据
     * @param operations
     * @return
     */
    private List<CartItemVO> getCartItems(BoundHashOperations operations){
        List<String> values = operations.values();
        if(values == null && values.size() == 0){
            return null;
        }
        List<CartItemVO> cartItemVOS = values.stream()
                .map(value ->JSON.parseObject(value, CartItemVO.class))
                .collect(Collectors.toList());

        return cartItemVOS;
    }


    /**
     *  根据threadLocal中的UserInfoTO信息来获取一个RedisHashOperations
     */
    private BoundHashOperations getRedisHashOperations() {
        UserInfoTO userInfoTO = CartInterceptor.threadLocal.get();
        String cartKey = "";
        if(userInfoTO!=null && userInfoTO.getXxlSsoUser()!=null && !StringUtils.isEmpty(userInfoTO.getXxlSsoUser().getUserid())){
            //登录了就以用户id作为redis的key
            cartKey = CART_PREFIX+userInfoTO.getXxlSsoUser().getUserid();
        }else{
            //未登录用户以cookie中的user-key作为redis的key
            cartKey = CART_PREFIX+userInfoTO.getUserKey();
        }

        //绑定一个key为cartKey的redis hash操作
        BoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(cartKey);

        return operations;
    }
}
